package org.reborn.demo

import com.jogamp.opengl.util._
import com.jogamp.opengl.util.awt.TextRenderer
import javax.media.opengl._
import javax.media.opengl.fixedfunc.GLLightingFunc
import javax.media.opengl.glu._
import java.awt._
import java.awt.event._
import org.reborn.Constants._

import org.reborn.engine.app._
import org.reborn.engine.enums.{UpdateFrequency, FaceMode, ArrayType}
import org.reborn.engine.model.mesh.{ConnectedEdge, MeshConnectivityUtil, MeshConnectivity, MeshUtil, ImmutableVertexData, VertexData, ImmutableMesh, MutableMesh, MeshShapes, Mesh}
import org.reborn.engine.render._
import org.reborn.engine.render.util._
import org.reborn.math.VectorF
import org.reborn.utils.HeightMapUtil._
import org.reborn.utils.Util._
import scala.{List, math}
import scala.collection.mutable.HashMap

object HeightMapMeshReduction extends GLApp {
  
  def main(args:Array[String]){
	
    val options = new GLAppOptions()
    options.title = "Normal Generation Test"
//    options.fullscreen = false
    options.debugGL = true
    
	HeightMapMeshReduction.startApp(options)
  }
  
  
  import GL._
  import GL2GL3._

  var dt = 0.0f;
  var dRot = 0.0f
  val text = new TextRenderer(new Font("SansSerif", Font.BOLD, 20))
  
  var calced = false;
  
  var meshId = -1;
  var mesh:MutableMesh = null;
  var reducedMeshId = -1;
  var reducedMesh:MutableMesh = null;
  
  var fpsAvg = 0.0f;
  
  var wire = false;
  
  var meshFaces = 0;
  var reducedMeshFaces = 0;
  
  override def initScene(drawable:GLAutoDrawable){
    val gl = drawable.getGL.getGL2
    import gl._
    
    import GLLightingFunc._
    
    glEnable(GL_DEPTH_TEST)
    glDepthFunc(GL_LEQUAL)
    glHint(GL2ES1.GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST)
    glClearColor(0.3f,0.3f,0.3f,1.0f)
    
    
    
    //Enable lighting
    glEnable(GL_LIGHTING)
    glShadeModel(GL_SMOOTH);
    glEnable(GL_COLOR_MATERIAL)
    glEnable(GL_LIGHT0)
    
    glLightfv(GL_LIGHT0,GL_POSITION,Array(0.0f,1.0f,1.0f,0.0f),0)
    glLightfv(GL_LIGHT0,GL_AMBIENT,Array(0.25f,0.25f,0.25f,1.0f),0)
    glLightfv(GL_LIGHT0,GL_DIFFUSE,Array(1.0f,1.0f,1.0f,1.0f),0)
    glLightfv(GL_LIGHT0,GL_SPECULAR,Array(1.0f,1.0f,1.0f,1.0f),0)
    
    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)
    glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,Array(1.0f,1.0f,1.0f,1.0f),0)
    glMaterialfv(GL_FRONT_AND_BACK,GL_EMISSION,Array(0.0f,0.0f,0.0f,0.0f),0)
    glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,100.0f)
    
	val hm = DiamondSquare(4,0.1f,0.45f,0.425f)
  
  	val vertArray = new Array[Float](hm.length * hm.length * 3)
    val colArray = new Array[Float](hm.length * hm.length * 3)
    val faceArray = new Array[Int]((hm.length-1) * (hm.length-1) * 6)
    
    var curIndex = 0;
    var curPoly = 0;
    for(x <- 0 until hm.length;
        y <- 0 until hm.length){
          val fX = -1.0f + (x.asInstanceOf[Float] / hm.length.asInstanceOf[Float]) * 2.0f
          val fZ = -1.0f + (y.asInstanceOf[Float] / hm.length.asInstanceOf[Float]) * 2.0f
          val fY = hm(x,y,true) * 2.0f - 0.25f
          
          vertArray(curIndex*3) = fX
          vertArray(curIndex*3+1) = fY
          vertArray(curIndex*3+2) = fZ
    
          var cR = 0.0f
          var cG = 0.0f
          var cB = 0.0f
          
          if(fY<=0.1f)          { cR=0.5f; cG=0.5f; cB=1.0f}
          if(fY>0.1f && fY<=.2) { cR=0.5f; cG=1.0f; cB=0.5f}
          if(fY>0.2f && fY<=.3) { cR=1.0f; cG=1.0f; cB=0.5f}
          if(fY>0.3f && fY<=.4) { cR=1.0f; cG=0.5f; cB=0.5f}
          if(fY>.4)             { cR=1.0f; cG=1.0f; cB=1.0f}  
          
          colArray(curIndex*3) = cR
          colArray(curIndex*3+1) = cG
          colArray(curIndex*3+2) = cB
          
          curIndex = curIndex+1;
          
          if(x < (hm.length -1) && y < (hm.length -1)){
            val leftBot = x * hm.length + y
            val rightBot = (x+1) * hm.length + y
            val rightTop = (x+1) * hm.length + y + 1
            val leftTop = x * hm.length + y + 1
            faceArray(curPoly) = leftBot
            faceArray(curPoly+1) = rightBot
            faceArray(curPoly+2) = rightTop
            faceArray(curPoly+3) = rightTop
            faceArray(curPoly+4) = leftTop
            faceArray(curPoly+5) = leftBot
            curPoly+=6;
          }
    }
    
    //Create a mesh  
//    mesh = new MutableMesh(FaceMode.Triangle,faceArray.length / 3, 100)
//    mesh.replaceFaceData(faceArray)
//    mesh.replaceVertData(ArrayType.Vertex,3,vertArray)
//    mesh.replaceVertData(ArrayType.Color,3,colArray)
    
    mesh = MeshShapes.Sphere(10,10,0.5f)
    mesh.appendVertData(ArrayType.Color,3,mesh(ArrayType.Normal).getDataArray)
    
    //Calculate the mesh normals and register the mesh
    val meshCon = MeshUtil.calcMeshConnectivity(mesh, true)
//    MeshUtil.calculateNormals(meshCon, 30.0f, Map())
    mesh = MeshUtil.MutableMesh(meshCon)
	meshId = util.meshRenderer.register(gl,mesh,UpdateFrequency.Rarely)
	
	//Reduce the mesh by 100 edges/polygons
	def edgeCost(edge:ConnectedEdge):Float = {
    	val dist = (edge.v2.dataMap(ArrayType.Vertex) - edge.v1.dataMap(ArrayType.Vertex)).magnitude
    	//val connEdges = edge.v1.getEdges.intersect(edge.v2.getEdges).size
    	10.0f + dist //- connEdges
    }
    def midpoint(edge:ConnectedEdge):HashMap[ArrayType.ArrayType,VectorF] = {
      var map:HashMap[ArrayType.ArrayType,VectorF] = HashMap()
      for(aType <- edge.v1.dataMap.keySet){
    	val newVert = (edge.v1.dataMap(aType) + ((edge.v2.dataMap(aType) - edge.v1.dataMap(aType)) / 2.0f))
    	aType match{
    		case ArrayType.Normal => map += (aType -> newVert.normalize)
    		case _ => map += (aType -> newVert)
    	}
      }
      map
    }
    def condition(removed:Int,curWeight:Float):Boolean = {
    	println(curWeight)
    	removed < 50
    }
	MeshUtil.reduceMesh(meshCon,edgeCost,midpoint,condition)
	reducedMesh = MeshUtil.MutableMesh(meshCon)
	//reducedMesh = mesh
	reducedMeshId = util.meshRenderer.register(gl,reducedMesh,UpdateFrequency.Rarely)
	
	meshFaces = mesh.faces;
	reducedMeshFaces = reducedMesh.faces;
  }
  
  override def updateLogic(seconds:Float){
    this.dt = seconds
    
    fpsAvg = ((25.0f * fpsAvg) + seconds) / 26.0f
    
    dRot = dRot + dt * 10.0f

    if((dRot.asInstanceOf[Int] / 100) % 2 == 0)
    	wire = false;
    else
    	wire = true;
  }
  
  override def renderScene(drawable:GLAutoDrawable){
    val gl = drawable.getGL.getGL2
    import gl._
    
    import GLLightingFunc._
    
    
    dRot = dRot + dt * 10.0f
    
    val shininess = 63.0f * math.cos(dt).asInstanceOf[Float] + 64.0f 
    glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,shininess)
    
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT)
    
    if(wire)
    	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    else
    	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    
    //Render the normal mesh
    glLoadIdentity();
    glTranslatef(-.75f,0.0f,-4.0f)
    glRotatef(50.0f,1.0f,0.0f,0.0f)
    glRotatef(dRot,0.0f,1.0f,0.0f)
    glRotatef(dRot,0.5f,0.0f,0.0f)
//    glTranslatef(0.0f,-.5f,0.0f)
    glColor3f(1.0f,1.0f,1.0f)
    util.meshRenderer.render(gl,meshId)
    
    
    //Render the reduced mesh
    glLoadIdentity();
    glTranslatef(.75f,0.0f,-4.0f)
    glRotatef(50.0f,1.0f,0.0f,0.0f)
    glRotatef(dRot,0.0f,1.0f,0.0f)
    glRotatef(dRot,0.5f,0.0f,0.0f)
 //   glTranslatef(0.0f,-.5f,0.0f)
    glColor3f(1.0f,1.0f,1.0f)
    util.meshRenderer.render(gl,reducedMeshId)
  
    
    if(wire)
    	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    	
    //Render the FPS text
    text beginRendering(drawable.getWidth, drawable.getHeight)
    text setColor(1.0f, 1.0f, 1.0f, 1.0f);
    text draw("" + 1.0f/dt,0,0);
    text draw("" + 1.0f/fpsAvg,0,16);
    text draw("" + dRot.asInstanceOf[Int],0,32);
    
    text draw("Faces= " + meshFaces,0,drawable.getHeight()-16)
    text draw("Faces= " + reducedMeshFaces,drawable.getWidth()/2,drawable.getHeight()-16)
    
    text endRendering
      
    glFlush
  }
}